<?php
/**
 * BaseIsv.php class file.
 * @author wudg <544038230@qq.com>
 * @date 2020/12/9- 上午10:36
 */

namespace common\components\api\isv;

use common\exception\ServicesException;
use console\components\helpers\ConsoleHelper;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ConnectException;
use yii\base\ErrorException;
use yii\base\Exception;
use yii\helpers\FileHelper;
use Yii;

class BaseIsv
{

    /**
     * 频率过快重复次数
     * @var int
     */
    private $tryTimes = 3;

    /**
     * 重试等待秒数
     * @var int
     */
    private $waitTimes = 1;


    /**
     * 评率超限错误码
     * @var int
     */
    private $LimitFrequencyCode = 70031;


    /**
     * api 异常原样返回白名单，在数组中的api 不对错误做异常抛出处理，直接原样返回，业务层按需处理
     * @var array
     */
    private $whiteAPIList = ['pdd.goods.add', 'pdd.goods.edit.goods.commit','pdd.goods.spec.id.get'];

    public function sign(string $action, int $mall_id = null){
        $tryTimes = 0;
        $responseArr = [];
        while ($tryTimes < $this->tryTimes) {
            try {
                $http = new Client([
                    //根域名
                    'base_uri' => Yii::$app->params['isv_url'],
                    //超时
                    'timeout' => Yii::$app->params['request_timeout'],

                ]);
                $pubParams = [
                    ['name' => 'type', 'contents' => $action],
                    ['name' => 'catalog', 'contents' => Yii::$app->params['project_tag']],
                    ['name' => 'access_token', 'contents' => Yii::$app->redis->get("token:".$mall_id.'_token')],
                    ['name' => 'timestamp', 'contents' => time()]
                ];


                $requestData = [
                    'multipart' => $pubParams
                ];
                //后去加密sign
                $sign_arr = array();
                if($requestData['multipart']){
                    foreach($requestData['multipart'] as $k =>$v){
                        $sign_arr[$v['name']] = $v['contents'];
                    }
                }
                $sign = $this->getsign($sign_arr);
                $requestData['multipart'][] = ['name' =>'token','contents'=> $sign];
                $response = $http->request('POST', 'getSign.php', $requestData);

                if ($response->getStatusCode() == 200) {
                    $responseStr = $response->getBody()->getContents();
                    $responseArr = json_decode($responseStr, 1);
                    if (!is_array($responseArr)) {
                        $this->writeIsvLog('返回内容异常：[' . json_encode($requestData, JSON_UNESCAPED_UNICODE) . ']' . $responseStr);
                        throw new ServicesException($responseStr);
                    }

                    /**
                     * 评率超限错误，重试继续来
                     */
                    if (isset($responseArr['error_response']['error_code']) && $responseArr['error_response']['error_code'] == $this->LimitFrequencyCode) {
                        $tryTimes++;
                        sleep($this->waitTimes);
                        ConsoleHelper::runInfo("请求接口频率超限，即将重试第{$tryTimes}次");
                        continue;
                    }

                    /**
                     * 有错误写入日志记录
                     */
                    if (isset($responseArr['error_response']) && !in_array($action, $this->whiteAPIList)) {
                        $this->writeIsvLog('接口返回异常请求参数：[' . json_encode($requestData, JSON_UNESCAPED_UNICODE) . ']');
                        $this->writeIsvLog('接口返回异常返回参数：[' . json_encode($responseArr['error_response'], JSON_UNESCAPED_UNICODE) . ']');
                        $errStr = $responseArr['error_response']['error_msg'] ?? '请求服务异常';
                        throw new ServicesException($errStr);
                    }
                    break;

                } else {
                    throw new ServicesException('请求isv错误，http状态码:' . $response->getStatusCode());
                }
            } catch (GuzzleException $e) {
                $tryTimes++;
                sleep($this->waitTimes);
                //$this->writeIsvLog('guzzle异常：[' . json_encode($args, JSON_UNESCAPED_UNICODE) . ']' . $e->getMessage());
                continue;
            }
        }


        return $responseArr;
    }

    /**
     * @param string $action
     * @param array $args
     * @param int $mall_id
     * @return mixed
     * @since 接口评率超限制时增加重试机会 by wudg 2021-01-28
     * @throws ServicesException
     */
    public function requestIsv(string $action, array $args = [], int $mall_id = null)
    {
        $tryTimes = 0;
        $responseArr = [];
        while ($tryTimes < $this->tryTimes) {
            try {
                $http = new Client([
                    //根域名
                    'base_uri' => Yii::$app->params['isv_url'],
                    //超时
                    'timeout' => Yii::$app->params['request_timeout'],

                ]);

                $pubParams = [
                    ['name' => 'type', 'contents' => $action],
                    ['name' => 'catalog', 'contents' => Yii::$app->params['project_tag']],
                    ['name' => 'timestamp', 'contents' => time()]
                ];


                if ($mall_id != null) {
                    $pubParams[] = ['name' => 'mall_id', 'contents' => $mall_id];
                } else {
                    $pubParams[] = ['name' => 'mall_id', 'contents' => Yii::$app->user->identity->mall_id];
                }


                $requestData = [
                    'multipart' => array_merge($args, $pubParams)
                ];
                //后去加密sign
//                $sign_arr = array();
//                if($requestData['multipart']){
//                    foreach($requestData['multipart'] as $k =>$v){
//                        $sign_arr[$v['name']] = $v['contents'];
//                    }
//                }
//                $sign = $this->getsign($sign_arr);
                //$requestData['multipart'][] = ['name' =>'token','contents'=> $sign];
                $response = $http->request('POST', 'api.php', $requestData);
                if ($response->getStatusCode() == 200) {
                    $responseStr = $response->getBody()->getContents();
                    $responseArr = json_decode($responseStr, 1);
                    if (!is_array($responseArr)) {
                        $this->writeIsvLog('返回内容异常：[' . json_encode($requestData, JSON_UNESCAPED_UNICODE) . ']' . $responseStr);
                        throw new ServicesException($responseStr);
                    }

                    /**
                     * 评率超限错误，重试继续来
                     */
                    if (isset($responseArr['error_response']['error_code']) && $responseArr['error_response']['error_code'] == $this->LimitFrequencyCode) {
                        $tryTimes++;
                        sleep($this->waitTimes);
                        ConsoleHelper::runInfo("请求接口频率超限，即将重试第{$tryTimes}次");
                        continue;
                    }

                    /**
                     * 有错误写入日志记录
                     */
                    if (isset($responseArr['error_response']) && !in_array($action, $this->whiteAPIList)) {
                        $this->writeIsvLog('接口返回异常请求参数：[' . json_encode($requestData, JSON_UNESCAPED_UNICODE) . ']');
                        $this->writeIsvLog('接口返回异常返回参数：[' . json_encode($responseArr['error_response'], JSON_UNESCAPED_UNICODE) . ']');
                        $errStr = $responseArr['error_response']['error_msg'] ?? '请求服务异常';
                        throw new ServicesException($errStr);
                    }
                    break;

                } else {
                    throw new ServicesException('请求isv错误，http状态码:' . $response->getStatusCode());
                }
            } catch (GuzzleException $e) {
                $tryTimes++;
                sleep($this->waitTimes);
                $this->writeIsvLog('guzzle请求异常：[' . json_encode($args, JSON_UNESCAPED_UNICODE) . ']' . $e->getMessage());
                continue;
            }
        }


        return $responseArr;
//        try {
//            $http = new Client([
//                //根域名
//                'base_uri' => Yii::$app->params['isv_url'],
//                //超时
//                'timeout' => Yii::$app->params['request_timeout'],
//
//            ]);
//
//            $pubParams = [
//                ['name' => 'type', 'contents' => $action],
//                ['name' => 'catalog', 'contents' => Yii::$app->params['project_tag']],
//            ];
//
//
//            if($mall_id != null)
//            {
//                $pubParams[] = ['name' =>'mall_id','contents'=> $mall_id];
//            }else{
//                $pubParams[] = ['name' =>'mall_id','contents'=> Yii::$app->user->identity->mall_id];
//            }
//
//
//            $requestData = [
//                'multipart' => array_merge($args,$pubParams)
//            ];
//
//
//            $response = $http->request('POST','/api.php',$requestData);
//            if($response->getStatusCode() == 200)
//            {
//                $responseStr = $response->getBody()->getContents();
//
//                $responseArr = json_decode($responseStr,1);
//
//                if(!is_array($responseArr))
//                {
//                    $this->writeIsvLog('返回内容异常：['.json_encode($requestData,JSON_UNESCAPED_UNICODE).']'.$responseStr);
//                    throw new ServicesException($responseStr);
//                }
//
//
//                /**
//                 * 有错误写入日志记录
//                 */
//                if(isset($responseArr['error_response']))
//                {
//                    $this->writeIsvLog('接口返回异常请求参数：['.json_encode($requestData,JSON_UNESCAPED_UNICODE).']');
//                    $this->writeIsvLog('接口返回异常返回参数：['.json_encode($responseArr['error_response'],JSON_UNESCAPED_UNICODE).']');
//                    $errStr = $responseArr['error_response']['error_msg'] ?? '请求服务异常';
//                    throw new ServicesException($errStr);
//                }
//                return $responseArr;
//            }else{
//                throw new ServicesException('请求isv错误，http状态码:'.$response->getStatusCode());
//            }
//        }catch (GuzzleException $e )
//        {
//            $this->writeIsvLog('guzzle异常：['.json_encode($args,JSON_UNESCAPED_UNICODE).']'.$e->getMessage());
//            throw new ServicesException($e->getMessage());
//        }

    }

    /**
     * @param string $action
     * @param array $args
     * @param int $mall_id
     * @return mixed
     * @since 接口评率超限制时增加重试机会
     * @throws ServicesException
     */
    public function requestPdd(string $action, array $arg = [], int $mall_id = null)
    {
        $tryTimes = 0;
        $responseArr = [];
        while ($tryTimes < $this->tryTimes) {
            try {
                $url = "https://gw-upload.pinduoduo.com/api/upload";
                $http = new Client([
                    //根域名
                    'base_uri' => $url,
                    //超时
                    'timeout' => Yii::$app->params['request_timeout'],

                ]);


                $args = [
                    ['name' => 'access_token', 'contents' => $arg['access_token']],
                    ['name' => 'client_id', 'contents' => $arg['client_id']],
                    ['name' => 'timestamp', 'contents' => $arg['timestamp']],
                    ['name' => 'type', 'contents' => $arg['type']],
                    ['name' => 'file', 'contents' => $arg['file'],'filename' => '1.jpg'],
                    ['name' => 'sign', 'contents' => $arg['sign']]

                ];
//                echo "<pre>";
//                print_r($args);
//                exit();
                $requestData = [
                    'multipart' => $args
                ];
                $response = $http->request('POST', '', $requestData);
                if ($response->getStatusCode() == 200) {
                    $responseStr = $response->getBody()->getContents();

                    $responseArr = json_decode($responseStr, 1);
                    if (!is_array($responseArr)) {
                        $this->writeIsvLog('返回内容异常：[' . json_encode($requestData, JSON_UNESCAPED_UNICODE) . ']' . $responseStr);
                        throw new ServicesException($responseStr);
                    }

                    /**
                     * 评率超限错误，重试继续来
                     */
                    if (isset($responseArr['error_response']['error_code']) && $responseArr['error_response']['error_code'] == $this->LimitFrequencyCode) {
                        $tryTimes++;
                        sleep($this->waitTimes);
                        ConsoleHelper::runInfo("请求接口频率超限，即将重试第{$tryTimes}次");
                        continue;
                    }

                    /**
                     * 有错误写入日志记录
                     */
                    if (isset($responseArr['error_response']) && !in_array($action, $this->whiteAPIList)) {
                        $this->writeIsvLog('接口返回异常请求参数：[' . json_encode($requestData, JSON_UNESCAPED_UNICODE) . ']');
                        $this->writeIsvLog('接口返回异常返回参数：[' . json_encode($responseArr['error_response'], JSON_UNESCAPED_UNICODE) . ']');
                        $errStr = $responseArr['error_response']['error_msg'] ?? '请求服务异常';
                        throw new ServicesException($errStr);
                    }
                    break;

                } else {
                    throw new ServicesException('请求isv错误，http状态码:' . $response->getStatusCode());
                }

                break;
            } catch (yii\base\Exception $e) {
                $tryTimes++;
                sleep($this->waitTimes);
                if (isset($arg['file'])) {
                    unset($arg['file']);
                }
                $this->writePddLog('(接口返回异常请求参数)：[' . json_encode($arg, JSON_UNESCAPED_UNICODE) . ']');
                //$this->writePddLog('(接口返回异常返回参数)：[' . json_encode($responseArr['error_response'], JSON_UNESCAPED_UNICODE) . ']');
                continue;
            }

        }
        return $responseArr;

    }

    /**
     * @param string $action
     * @param array $args
     * @param int $mall_id
     * @return mixed
     * @since 接口评率超限制时增加重试机会
     * @throws ServicesException
     */
    public function requestPddToken(string $action, array $arg = [], int $mall_id = null)
    {
        $tryTimes = 0;
        $responseArr = [];
        while ($tryTimes < $this->tryTimes) {
            try {
                $params = array();
                $params['access_token'] = Yii::$app->redis->get($mall_id . '_token');
                $params['client_id'] = Yii::$app->params['client_id'];
                $params['timestamp'] = time();
                $params['type'] = $action;
                $params = array_merge($params, $arg);
                ksort($params);
                $str = Yii::$app->params['client_secret'];
                foreach ($params as $key => $value) {
                    if (gettype($params[$key]) != 'object') {
                        $str .= $key . $value;
                    }
                }
                $str .= Yii::$app->params['client_secret'];
                $params['sign'] = strtoupper(md5($str));

                $url = "http://gw-api.pinduoduo.com/api/router";
                if ($params['type'] == 'pdd.goods.filespace.image.upload') {
                    $url = "https://gw-upload.pinduoduo.com/api/upload";
                }

                if (isset($params['just_sign']) && $params['just_sign'] == 1) {
                    return $params['sign'];
                }

                $headers = [
                    "Content-type: application/json;charset='utf-8'",
                ];
                $params = json_encode($params);
                $result = $this->http($url, $params, $headers);
                $responseArr = json_decode($result, 1);
                if (!is_array($responseArr)) {
                    $this->writeIsvLog('返回内容异常：[' . json_encode($params, JSON_UNESCAPED_UNICODE) . ']' . $result);
                    throw new ServicesException($result);
                }
                /**
                 * 评率超限错误，重试继续来
                 */
                if (isset($responseArr['error_code']) && $responseArr['error_code'] == $this->LimitFrequencyCode) {
                    $tryTimes++;
                    sleep($this->waitTimes);
                    ConsoleHelper::runInfo("请求接口频率超限，即将重试第{$tryTimes}次");
                    continue;
                }
//                /**
//                 * 有错误写入日志记录
//                 */
//                if (isset($responseArr['error_response']) && !in_array($action, $this->whiteAPIList)) {
//                    $this->writePddLog('接口返回异常请求参数：[' . json_encode($params, JSON_UNESCAPED_UNICODE) . ']');
//                    $this->writePddLog('接口返回异常返回参数：[' . json_encode($responseArr['error_response'], JSON_UNESCAPED_UNICODE) . ']');
//                    $errStr = $responseArr['error_response']['error_msg'] ?? '请求服务异常';
//                    throw new ServicesException($errStr);
//                }
                if (isset($responseArr['error_response']) && $responseArr['error_response']['error_code'] == '10019' && $action<>'pdd.pop.auth.token.refresh') {
                    $result_array = (new Goods())->pddGoodsRefresh(Yii::$app->redis->get($mall_id . '_refesh_token'), $mall_id);
                    if($result_array){
                        if (isset($result_array['pop_auth_token_refresh_response'])) {
                            $result_array = $result_array['pop_auth_token_refresh_response'];
                            (new BaseIsv())->setToken($mall_id, $result_array['access_token']);
                        }
                    }
                    break;
                }

                break;
            } catch (yii\base\Exception $e) {
                $tryTimes++;
                sleep($this->waitTimes);
                if (isset($params['image'])) {
                    unset($params['image']);
                }
                $this->writePddLog('(接口返回异常请求参数)：[' . json_encode($params, JSON_UNESCAPED_UNICODE) . ']');
                $this->writePddLog('(接口返回异常返回参数)：[' . json_encode($responseArr['error_response'], JSON_UNESCAPED_UNICODE) . ']');
                continue;
            }

        }
        return $responseArr;

    }

    /**
     * http请求
     */
    function http($url, $data = false, $headers = false)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        if ($data) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        if ($headers) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        //执行
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }


    /**
     * 设置accessToken
     * @param int $mall_id
     * @param string $token
     * @param int $expire
     */
    public function setToken(int $mall_id, string $token, $expire = 86400)
    {
        Yii::$app->redis->setex($mall_id . '_token', $expire, $token);
    }


    /**
     * @param int $mall_id
     * 获取accessToken
     * @return string
     */
    public function getToken(int $mall_id = null): string
    {
        if ($mall_id == null) {
            $mall_id = Yii::$app->user->identity->mall_id;
        }
        return Yii::$app->redis->get($mall_id . '_token');
    }


    protected function formatArgs(array $args)
    {
        $requstArgs = [];
        foreach ($args as $k => $item) {
            $tmp = ['name' => $k, 'contents' => $item];

            array_push($requstArgs, $tmp);
        }
        return $requstArgs;
    }


    /**
     * 文件日志记录
     * @param $msg
     * @throws ServicesException
     */
    public function writeIsvLog($msg)
    {

        try {
            $path = Yii::getAlias('@runtime/api/isv/');

            //目录如果不存就先创建目录
            if (!is_dir($path)) {
                FileHelper::createDirectory($path);
            }

            $cp = fopen($path . 'log-' . date('Ymd') . '.log', "a+");
            $txt = "【" . date('Y-m-d H:i:s', time()) . "】---" . $msg . "\r\n";
            fwrite($cp, $txt);
            fclose($cp);
        } catch (Exception $e) {
            throw new ServicesException($e->getMessage());
        }

    }

    /**
     * 文件日志记录
     * @param $msg
     * @throws ServicesException
     */
    public function writePddLog($msg)
    {

        try {
            $path = Yii::getAlias('@runtime/api/pdd/');

            //目录如果不存就先创建目录
            if (!is_dir($path)) {
                FileHelper::createDirectory($path);
            }

            $cp = fopen($path . 'log-' . date('Ymd H') . '.log', "a+");
            $txt = "【" . date('Y-m-d H:i:s', time()) . "】---" . $msg . "\r\n";
            fwrite($cp, $txt);
            fclose($cp);
        } catch (Exception $e) {
            throw new ServicesException($e->getMessage());
        }

    }

    public function writeIsvLog1($msg)
    {

        try {
            $path = Yii::getAlias('@runtime/api/isv1/');

            //目录如果不存就先创建目录
            if (!is_dir($path)) {
                FileHelper::createDirectory($path);
            }

            $cp = fopen($path . 'log-' . date('Ymd') . '.log', "a+");
            $txt = "【" . date('Y-m-d H:i:s', time()) . "】---" . $msg . "\r\n";
            fwrite($cp, $txt);
            fclose($cp);
        } catch (Exception $e) {
            throw new ServicesException($e->getMessage());
        }

    }

    public function getsign($params){
        ksort($params);
        $code = '@';
        foreach($params as $k => $item)
        {
            $code .= $k.$item;
        }
        $code .= '@';
        return strtoupper(md5($code));
    }


}